home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacHack 1996
/
MacHack 1996.toast
/
Hacks
/
Hacks ’87
/
Source ƒ.sit
/
Source ƒ
/
C ƒ
/
CITADEL BBS 'C' SRC
/
MODEM.C
< prev
next >
Wrap
C/C++ Source or Header
|
1987-01-14
|
36KB
|
1,249 lines
/************************************************************************/
/* modem.c */
/* */
/* modem code for Citadel bulletin board system */
/* NB: this code is rather machine-dependent: it will typically */
/* need some twiddling for each new installation. */
/* 82Nov05 CrT */
/************************************************************************/
#define NO_CRC
/************************************************************************/
/* history */
/* */
/* 85Nov09 HAW Warning bell before timeout. */
/* 85Oct27 HAW Cermetek support eliminated. */
/* 85Oct18 HAW 2400 support. */
/* 85Sep15 HAW Put limit in ringSysop(). */
/* 85Aug17 HAW Update for gotCarrier(). */
/* 85Jul05 HAW Insert fix code (Brian Riley) for 1200 network. */
/* 85Jun11 HAW Fix readFile to recognize loss of carrier. */
/* 85May27 HAW Code for networking time out. */
/* 85May06 HAW Code for daily timeout. */
/* 85Mar07 HAW Stick in Sperry PC mods for MSDOS. */
/* 85Feb22 HAW Upload/download implemented. */
/* 85Feb20 HAW IMPERVIOUS flag implemented. */
/* 85Feb17 HAW Baud change functions installed. */
/* 85Feb09 HAW and Sr. Chat bug analyzed by Sr. */
/* 85Jan16 JLS fastIn modified for CR being first character from modem.*/
/* 85Jan04 HAW Code added but not tested for new WC functions. */
/* 84Sep12 HAW Continue massacre of portability -- bye, pMIReady. */
/* 84Aug30 HAW Wheeee!! MS-DOS time!! */
/* 84Aug22 HAW Compilation directive for 8085 chips inserted. */
/* 84Jul08 JLS & HAW ReadFile() fixed for the 255 rollover. */
/* 84Jul03 JLS & HAW All references to putCh changed to putChar. */
/* 84Jun23 HAW & JLS Local unused variables zapped. */
/* 84Mar07 HAW Upgrade to BDS 1.50a begun. */
/* 83Mar01 CrT FastIn() ignores LFs etc -- CRLF folks won't be trapped.*/
/* 83Feb25 CrT Possible fix for backspace-in-message-entry problem. */
/* 83Feb18 CrT fastIn() upload mode cutting in on people. Fixed. */
/* 82Dec16 dvm modemInit revised for FDC-1, with kludge for use with */
/* Big Board development system */
/* 82Dec06 CrT 2.00 release. */
/* 82Nov15 CrT readfile() & sendfile() borrowed from TelEdit.c */
/* 82Nov05 CrT Individual history file established */
/************************************************************************/
#include "ctdl.h"
/************************************************************************/
/* Contents */
/* */
/* BBSCharReady() returns true if user input is ready */
/* check_CR() scan input for carriage returns */
/* doWC() initializes WC transfers */
/* # fastIn() kludge code compiling other stuff inline*/
/* Find_baud() does flip flop search for baud */
/* getCh() bottom-level console-input filter */
/* # getMod() bottom-level modem-input filter */
/* iChar() top-level user-input function */
/* interact() chat mode */
/* interpret() interprets a configuration routine */
/* KBReady() returns TRUE if a console char is ready */
/* MIReady() check MS-DOS interrupt for data */
/* modIn() returns a user char */
/* modemInit() top-level initialize-all-modem-stuff */
/* # mputChar() Try not to allow ^C aborts ... */
/* oChar() top-level user-output function */
/* # outMod() bottom-level modem output */
/* pause() pauses for N/100 seconds */
/* readFile() accept a file using WC protocol */
/* recieve() read modem char or time out */
/* ringSysop() signal chat-mode request */
/* sendWCChar() send file with WC-protocol handshaking */
/* shortPause() Brian Riley's 1200 fix code. */
/* */
/* # == routines you should certainly check when porting system */
/************************************************************************/
/************************************************************************/
/* External variable declarations in MODEM.C */
/************************************************************************/
char justLostCarrier = FALSE; /* Modem <==> rooms connection */
char newCarrier = FALSE; /* Just got carrier */
char onConsole; /* Who's in control?!? */
int outPut = NORMAL;
unsigned char modStat; /* Whether modem had carrier LAST time */
/* you checked. */
static unsigned /* Place to save data on check ... */
char InData;
static char KBData = 0; /* Data from keyboard */
char whichIO = CONSOLE; /* CONSOLE or MODEM */
#ifdef NEED_VISIBLE
char visibleMode; /* make non-printables visible? */
#endif
char haveCarrier; /* set if DCD == TRUE */
char textDownload; /* read host files, TRUE => ASCII */
char echo; /* Either NEITHER, CALLER, or BOTH */
char echoChar; /* What to echo with if echo == NEITHER */
char usingWCprotocol; /* True during WC protocol transfers */
int upDay; /* Day system was brought up */
char nextDay; /* Come down tomorrow rather than today?*/
int timeCrash;
char IBMinterrupts = FALSE;
char anyEcho = TRUE;
char warned;
#ifdef MSDOS
#define COMBUF_SIZE 5000
unsigned char old_inta01;
unsigned char combuf[COMBUF_SIZE];
int cptr, head;
#endif
static struct regval writereg = {0, 0, 0, 0, 0, 0, 0, 0};
static struct regval readreg = {0x0100, 0, 0, 0, 0, 0, 0, 0};
static struct regval inreg;
static char *rates[] = {
"300",
"1200",
"2400",
"Unknown"} ;
/* WC stuff */
#define MINUTE 60
unsigned char WCSecNum;
char WCBuf[SECTSIZE];
unsigned char WCChar;
char WCError;
char CRCmode;
/************************************************************************/
/* External variable definitions for MODEM.C */
/************************************************************************/
extern struct msgB msgBuf; /* Message buffer */
extern struct config cfg; /* Configuration variables */
extern FILE *upfd;
extern char termLF; /* Linefeeds? */
extern char prevChar; /* previous char */
extern char termUpper; /* Handle only UC? */
extern char debug; /* debug flag */
extern char outFlag; /* output flag */
extern char ExitToMsdos; /* Kill program flag */
extern int exitValue;
extern char inNet;
extern int acount;
#define AUDIT 9000
extern char audit[AUDIT];
/************************************************************************/
/* External function definitions for MODEM.C */
/************************************************************************/
unsigned char inportb();
unsigned char interpret();
unsigned char modIn();
unsigned char bdos();
unsigned char inp();
char getCh();
char KBReady();
long getNumber();
char getMod();
/************************************************************************/
/* The principal dependencies: */
/* */
/* iChar modIn outMod */
/* modIn getMod getCh mIReady kBReady outMod carrDetect */
/* getMod */
/* getCh */
/* mIReady */
/* kBReady */
/* carrDetect */
/* */
/* oChar outMod */
/* outMod mOReady */
/************************************************************************/
/************************************************************************/
/* BBSCharReady() returns TRUE if char is available from user */
/* NB: user may be on modem, or may be sysop in CONSOLE mode */
/************************************************************************/
char BBSCharReady()
{
return (((haveCarrier && whichIO == MODEM) && MIReady()) ||
(whichIO == CONSOLE && KBReady()));
}
/************************************************************************/
/* doWC() initializes the system for a WC download */
/************************************************************************/
doWC(mode)
char mode;
{
int i, m;
if (mode == STARTUP) { /* Setup globals for coming fun */
if (!cfg.debug && !inNet)
printf("A WC download is in progress\n");
WCSecNum = 1;
WCChar = 0;
WCError = FALSE;
CRCmode = FALSE;
i = 0;
while (1) {
m = receive(MINUTE);
if (m == ERROR || m == CAN)
return FALSE;
if (m == NAK)
return TRUE;
if (m == 'C') {
CRCmode = TRUE;
printf("CRC mode\n");
return TRUE;
}
if (++i == ERRORMAX)
return FALSE;
}
}
else {
if (WCError) return;
for (; WCChar != 0 && sendWCChar(' '); )
; /* Final sector */
for (i = 0; i < ERRORMAX; i++) {
outMod(EOT);
if (receive(MINUTE) == ACK || !haveCarrier) break;
}
}
}
/************************************************************************/
/* Find_Baud() Finds the baud from sysop and user supplied data. */
/************************************************************************/
Find_baud()
{
char noGood = TRUE;
int Time = 0;
int baudRunner; /* Only try for 60 seconds */
while (MIReady()) getMod(); /* Clear garbage */
baudRunner = 0;
while (gotCarrier() && noGood && Time < 120) {
Time++;
interpret(cfg.pBauds[baudRunner]);
noGood = check_CR();
if (noGood) baudRunner = (baudRunner + 1) % (cfg.sysBaud + 1);
}
logMessage(BAUD, rates[baudRunner], FALSE);
return !noGood;
}
/************************************************************************/
/* check_CR() Checks for CRs from the data port for half a second. */
/************************************************************************/
check_CR()
{
int i;
for (i = 0; i < 50; i++) {
pause(1);
if (MIReady())
if (getMod() == '\r')
return FALSE;
}
return TRUE;
}
/************************************************************************/
/* getCh() reads a console char */
/* In CONSOLE mode, CRs are changed to newlines */
/* Rubouts are changed to backspaces */
/* Returns: resulting char */
/************************************************************************/
char getCh()
{
char temp;
if (KBData != 0) {
temp = KBData;
KBData = 0;
return temp;
}
return bdos(7);
}
/************************************************************************/
/* getMod() is bottom-level modem-input routine */
/* kills any parity bit */
/* rubout -> backspace */
/* CR -> newline */
/* other nonprinting chars -> blank */
/* Returns: result */
/************************************************************************/
char getMod()
{
return inp() & 0x7F;
}
/************************************************************************/
/* iChar() is the top-level user-input function -- this is the */
/* function the rest of Citadel uses to obtain user input */
/************************************************************************/
char iChar()
{
char c;
if (justLostCarrier) return 0; /* ugly patch */
c = cfg.filter[modIn()];
/* AUDIT CODE */
if (!onConsole) {
audit[acount] = c;
acount = (acount + 1) % AUDIT;
}
switch (echo) {
case BOTH:
if (haveCarrier) {
if (c == '\n') doCR();
else outMod(c);
}
mputChar(c); /* Let putChar decide if it should go on console */
break;
case CALLER:
if (whichIO == MODEM) {
if (c == '\n') doCR();
else outMod(c);
} else {
mputChar(c);
}
break;
case NEITHER:
if (echoChar != '\0') {
if (whichIO == MODEM) {
if (c == '\n') doCR();
else outMod(echoChar);
}
else {
if (c == '\n') doCR();
else mputChar(echoChar);
}
}
break;
}
return(c);
}
/************************************************************************/
/* inp() reads data from port. Should not be called if there is */
/* no data present (for good reason). */
/************************************************************************/
unsigned char inp()
{
unsigned char temp;
int k;
if (!cfg.IBM_or_clone) {
temp = InData;
InData = 0;
return temp;
}
else {
if (!IBMinterrupts)
return inportb(cfg.modem_data);
k = cptr;
if (k==head)
return 0;
if (k>head)
return (combuf[head++]);
if (head < COMBUF_SIZE)
return (combuf[head++]);
head = 0;
if (head < k)
return (combuf[head++]);
printf("ERROR\n");
}
}
/************************************************************************/
/* interact() allows the sysop to interact directly with */
/* whatever is on the modem. dvm 9-82 */
/************************************************************************/
interact()
{
char c, lineEcho, lineFeeds, localEcho;
int yr, dy, hr, mn;
char last = 0;
char *mon;
c = 0;
printf(" Direct modem-interaction mode\n");
lineEcho = conGetYesNo("Echo to modem" );
localEcho = conGetYesNo("Echo keyboard" );
lineFeeds = conGetYesNo("Echo CR as CRLF" );
printf("<ESC> to exit\n");
/* incredibly ugly code. Rethink sometime: */
while (c != SPECIAL) {
c = 0;
if (MIReady()) {
c = getMod();
if (c != '\r') c = cfg.filter[c];
if (c != '\r') {
if (lineEcho && c != ESC) outMod(c);
mputChar(c);
} else {
if (!lineFeeds) {
if (lineEcho) outMod('\r');
mputChar('\r');
mputChar('\n');
} else {
if (lineEcho) {
outMod('\r');
outMod('\n');
}
mputChar('\r');
mputChar('\n');
}
}
}
else if (KBReady()) {
/* c = cfg.filter[getCh()]; */
if ((c = getCh() & 0x7f) == '\r') c = '\n';
if (c != NEWLINE) {
if (localEcho) mputChar(c);
if (c != ESC && c != '\\') outMod(c);
else {
if (last == '\\') {
outMod(c);
c = 0;
}
}
last = c;
} else {
if (!lineFeeds) {
if (localEcho) mputChar('\r');
outMod('\r');
} else {
if (lineEcho) {
mputChar('\r');
mputChar('\n');
}
outMod('\r');
outMod('\n');
}
}
}
else if (cfg.netParticipant) {
getDate(&yr, &mon, &dy, &hr, &mn);
if (dy % cfg.dayDiv == 0)
if (hr == cfg.netHour - 1 && mn >= 55 && !warned) {
warned = TRUE;
outFlag = IMPERVIOUS;
mPrintf("\n WARNING: System going down at %d AM\n ",
cfg.netHour);
outFlag = OUTOK;
}
else if (inNetTime(hr, mn)) {
c = SPECIAL;
}
}
}
}
/************************************************************************/
/* interpret() interprets a configuration routine */
/* Returns byte value computed */
/************************************************************************/
unsigned char interpret(instruction)
int instruction;
{
union {
unsigned char **pp;
int *pi;
unsigned char *pc;
} instr;
unsigned char accum, bite; /* our sole accumulator */
char *prompt;
int lowLim, topLim;
int comint();
/* Deactivate IBM interrupts for a moment */
if (cfg.IBM_or_clone && IBMinterrupts) {
bite = inportb(INTA01);
outportb(INTA01, old_inta01);
}
instr.pc = &cfg.codeBuf[instruction];
while (TRUE) {
switch (*instr.pc++) {
case RET:
if (cfg.IBM_or_clone && IBMinterrupts)
outportb(INTA01, bite);
return accum; break;
case ANDI: accum &= *instr.pc++; break;
case INP: accum = inportb(*instr.pi++); break;
case XORI: accum ^= *instr.pc++; break;
case LOAD: accum = *(*instr.pp++); break;
case LOADI: accum = *instr.pc++; break;
case LOADX: accum = cfg.scratch[*instr.pc++]; break;
case ORI: accum |= *instr.pc++; break;
case OUTP: outportb(*instr.pi++, accum); break;
case PAUSEI: pause(*instr.pc++); break;
case STORE: *(*instr.pp++) = accum; break;
case STOREX: cfg.scratch[*instr.pc++] = accum; break;
case OPRNUMBER:
prompt = instr.pc;
while(*instr.pc++); /* step over prompt */
lowLim = *instr.pc++;
topLim = *instr.pc++;
accum = (int) getNumber(prompt, (ulong) lowLim, (ulong) topLim);
break;
case OUTSTRING:
while(*instr.pc) {
pause(5); /* SmartModem can't handle 300 baud */
outMod(*instr.pc++); /* output string */
}
instr.pc++; /* skip null */
break;
default:
printf("intrp-no opcod%d", *(instr.pc-1));
break;
}
}
if (cfg.IBM_or_clone && IBMinterrupts)
outportb(INTA01, bite);
}
/************************************************************************/
/* KBReady() returns TRUE if a console char is ready */
/************************************************************************/
char KBReady()
{
if (KBData) return TRUE;
return (KBData = bdos(6, 0xFF));
}
/************************************************************************/
/* MIReady() Ostensibly checks to see if input from modem ready */
/************************************************************************/
MIReady()
{
unsigned char regVal;
if (!cfg.IBM_or_clone) {
#define MOD_FUNC 0x4e
#define BIOSSEG 0x40
if (InData != 0)
return TRUE;
if (farcall(MOD_FUNC, BIOSSEG, &readreg, &inreg) & 1)
return FALSE;
InData = inreg.ax & 0xFF; /* Strip high byte of result */
return TRUE;
}
else {
if (!IBMinterrupts) {
regVal = inportb(cfg.modem_status) & 1;
return regVal;
}
/* else */
return (cptr != head);
}
}
/************************************************************************/
/* modemClose() Responsible for shutting down I/O */
/************************************************************************/
modemClose()
{
if (cfg.IBM_or_clone && IBMinterrupts) {
outportb(INTA01, old_inta01); /* Kill vector */
intrrest(COMINT_VEC);
}
}
/************************************************************************/
/* modemInit() is responsible for all modem-related initialization */
/* at system startup */
/* Globals modified: haveCarrier visibleMode */
/* whichIO modStat */
/* ExitToMsDos justLostCarrier */
/* modified 82Dec10 to set FDC-1 SIO-B clock speed at */
/* 300 baud -dvm */
/************************************************************************/
modemInit()
{
static char second = FALSE;
msgBuf.Ooops = '\n';
#ifdef NEED_VISIBLE
visibleMode = FALSE;
#endif
interpret(cfg.pInitPort);
interpret(cfg.pHangUp);
if (!second) setInterrupts();
second = TRUE;
haveCarrier = modStat = gotCarrier();
}
#ifdef MSDOS
setInterrupts() /* ASSUME COM1 for now */
{
unsigned char bite, inportb();
int comint();
if (!cfg.IBM_or_clone || !IBMinterrupts) return ;
outportb(/*MCR*/0x3fc, 0x0b);
inportb(/*MSR*/cfg.modem_status, bite);
inportb(/*LSR*/cfg.line_status, bite);
inportb(/*IOR*/cfg.modem_data, bite);
bite = inportb(/*LCR*/0x3fb);
bite &= 0x7f;
outportb(/*LCR*/0x3fb, bite);
outportb(/*IER*/0x3f9, 0x01);
old_inta01 = bite = inportb(INTA01);
bite &= 0xef;
outportb(INTA01, bite);
cptr = head = 0;
intrinit(comint, 5000, COMINT_VEC);
}
#endif
comint()
{
unsigned char inportb(), cbuf;
unsigned int lsr_data;
lsr_data = inportb(/*LSR*/cfg.line_status);
if ((lsr_data & 0x01) == 0) {
outportb(/*MCR*/0x3fc, 0x0b);
outportb(/*INTA00*/0x20, EOI);
return;
}
cbuf = inportb(/*RX_BUFFER*/cfg.modem_data);
if (cptr >= COMBUF_SIZE)
cptr = 0;
combuf[cptr++] = cbuf;
outportb(/*MCR*/0x3fc, 0x0b);
outportb(/*INTA00*/0x20, EOI);
return ;
}
/************************************************************************/
/* modIn() toplevel modem-input function */
/* If DCD status has changed since the last access, reports */
/* carrier present or absent and sets flags as appropriate. */
/* In case of a carrier loss, waits 20 ticks and rechecks */
/* carrier to make sure it was not a temporary glitch. */
/* If carrier is newly received, returns newCarrier = TRUE; if */
/* carrier lost returns 0. If carrier is present and state */
/* has not changed, gets a character if present and */
/* returns it. If a character is typed at the console, */
/* checks to see if it is keyboard interrupt character. If */
/* so, prints short-form console menu and awaits next */
/* keyboard character. */
/* Globals modified: carrierDetect modStat haveCarrier */
/* justLostCarrier whichIO ExitToMsDos */
/* visibleMode */
/* Returns: modem or console input character, */
/* or above special values */
/************************************************************************/
unsigned char modIn()
{
unsigned char c, logVal;
unsigned long timer, temp, signal;
int yr, dy, hr, mn;
char *mon;
temp = cfg.megaHz;
timer = (HITIMEOUT * temp);
logVal = 0;
signal = (unsigned long) (HITIMEOUT * temp) / 21;
while (TRUE) {
if ((whichIO==MODEM) && (c=gotCarrier()) != modStat) {
/* carrier changed */
if (c) { /* carrier present */
if (cfg.search_baud == 1) {
if (Find_baud()) {
printf("Carr-detect\n");
warned = FALSE;
haveCarrier = TRUE;
modStat = c;
newCarrier = TRUE;
justLostCarrier = FALSE;
}
else
interpret(cfg.pHangUp);
}
else {
printf("Carr-detect\n");
interpret(cfg.pBauds[interpret(cfg.pCheckBaud)]);
logMessage(BAUD, rates[interpret(cfg.pCheckBaud)], FALSE);
warned = FALSE;
haveCarrier = TRUE;
modStat = c;
newCarrier = TRUE;
justLostCarrier = FALSE;
pause(200);
}
return(0);
} else {
pause(200); /* confirm it's not a glitch */
if (!gotCarrier()) { /* check again */
printf("Carr-loss\n");
logMessage(CARRLOSS, "", logVal);
haveCarrier = FALSE;
modStat = FALSE;
justLostCarrier = TRUE;
return(0);
}
}
}
if (MIReady()) {
if (haveCarrier) {
c = getMod();
if (whichIO == MODEM) return c;
}
}
if (KBReady()) {
c = getCh();
if (whichIO == CONSOLE) return(c);
else {
if (c == SPECIAL) {
printf("CONSOLE mode\n ");
whichIO = CONSOLE;
setUp(FALSE);
warned = FALSE;
return 0;
}
}
}
if (cfg.netParticipant) {
getDate(&yr, &mon, &dy, &hr, &mn);
if (dy % cfg.dayDiv == 0)
if (hr == cfg.netHour - 1 && mn >= 55 && !warned
&& (haveCarrier || whichIO == CONSOLE)) {
warned = TRUE;
outFlag = IMPERVIOUS;
mPrintf("\n WARNING: System going down at %d AM\n ",
cfg.netHour);
outFlag = OUTOK;
}
else if (inNetTime(hr, mn)) {
if (haveCarrier || whichIO == CONSOLE) {
outFlag = IMPERVIOUS;
mPrintf("Going to network mode. Bye!\n ");
terminate(TRUE);
outFlag = OUTOK;
return 0; /* This should do what we want */
}
netController();
warned = FALSE;
}
}
/* No need for speed here */
if (!haveCarrier && whichIO != CONSOLE && cfg.dailyTimeout) {
getDate(&yr, &mon, &dy, &hr, &mn);
if (!nextDay) {
if (hr >= timeCrash) {
ExitToMsdos = TRUE;
exitValue = TIME_EXIT;
return 0;
}
}
else {
if (dy != upDay && hr >= timeCrash) {
ExitToMsdos = TRUE;
exitValue = TIME_EXIT;
return 0;
}
}
}
/* check for no input. (Short-circuit evaluation, remember!) */
if (whichIO==MODEM && haveCarrier && !--timer) {
mPrintf("Sleeping? Call again :-)");
interpret(cfg.pHangUp);
logVal = 't';
}
else if (whichIO==MODEM && haveCarrier && signal == timer)
oChar(BELL);
}
}
/************************************************************************/
/* oChar() is the top-level user-output function */
/* sends to modem port and console both */
/* does conversion to upper-case etc as necessary */
/* in "debug" mode, converts control chars to uppercase letters */
/* Globals modified: prevChar */
/************************************************************************/
oChar(c)
char c;
{
prevChar = c; /* for end-of-paragraph code */
if (outFlag != OUTOK && /* s(kip) mode */
outFlag != IMPERVIOUS)
return;
if (termUpper) c = toupper(c);
#ifdef NEED_VISIBLE
if (debug) c = visible(c);
#endif
if (c == NEWLINE) c = ' '; /* doCR() handles real newlines */
/* show on console */
if (usingWCprotocol != WC_NONE) {
sendWCChar(c);
}
else if (outPut == DISK) {
putc(c, upfd);
}
else {
mputChar(c);
if (haveCarrier)
outMod(c);
}
}
/************************************************************************/
/* outMod stuffs a char out the modem port */
/************************************************************************/
outMod(c)
unsigned char c;
{
char notReady;
unsigned char inportb();
int flag;
unsigned char regVal;
if (!cfg.IBM_or_clone) {
writereg.ax = c; /* AH is set automatically to 0 */
while (farcall(MOD_FUNC, BIOSSEG, &writereg, &inreg) & 0x01)
;
}
else {
#ifdef OLD_STYLE
notReady = TRUE;
while (notReady) {
regVal = inportb(cfg.line_status) & 32;
notReady = (regVal == 0);
}
outportb(cfg.modem_data, c);
#else
outportb(/*MCR*/0x3fc, 0x0b);
wait1:
flag = inportb(/*LSR*/ cfg.line_status);
if ((flag & 0x20) == 0) goto wait1;
outportb(/*IOR*/cfg.modem_data, c);
return(0);
#endif
}
}
/************************************************************************/
/* pause() busy-waits N/100 seconds */
/************************************************************************/
pause(i)
int i;
{
int j;
#define SECONDSFACTOR 102 /* Seems to be right for 8088 */
for (; i; i--) {
for (j=(SECONDSFACTOR*cfg.megaHz); j; j--);
}
}
/************************************************************************/
/* mputChar() */
/************************************************************************/
mputChar(c)
char c;
{
if (c == BELL && cfg.noChat && !onConsole)
return;
if (!(whichIO == CONSOLE || onConsole) && !anyEcho)
return;
if (c != ESC &&
(echo == BOTH || (whichIO == CONSOLE && (echo != NEITHER || echoChar)))) {
bdos(6, c);
if (c == '\n')
mputChar('\r');
}
}
/************************************************************************/
/* receive() gets a modem character, or times out ... */
/* Returns: char on success else ERROR */
/************************************************************************/
int receive(seconds)
int seconds;
{
unsigned count;
count = seconds * 500;
while (!MIReady() && --count)
shortPause();
if (count && gotCarrier()) return inp();
return(ERROR);
}
/************************************************************************/
/* readFile() accepts a file from modem using Ward Christensen's */
/* protocol. (ie, compatable with xModem, modem7, yam, modem2...) */
/* Returns: TRUE on successful transfer, else FALSE */
/************************************************************************/
char readFile(pc)
int (*pc)(); /* pc will accept the file one character at a time. */
/* returns ERROR on any problem, and closes the file */
/* when handed ERROR as an argument. */
{
char errMess[100];
int i, firstchar, lastSector, thisSector, thisComplement, tries;
int toterr, checksum, recVal;
unsigned char badSector, writeError;
unsigned char sectBuf[SECTSIZE];
unsigned char *nextChar;
#ifndef NO_CRC
char goodCRC, errorDet;
unsigned totalCRC, temp;
char charIn;
int loCRC;
char acceptable[7];
acceptable[0] = EOT;
acceptable[1] = SOH;
acceptable[2] = 0;
#endif
lastSector = 0;
tries = 0;
toterr = 0;
writeError = FALSE;
while (MIReady()) inp(); /* clear garbage*/
if (cfg.debug) printf("In readFile\n"); /*****/
if (!inNet)
printf((cfg.debug) ? "Awaiting #0 (Try=0, Errs=0) \r" :
"A WC upload in progress\n");
#ifdef NO_CRC
outMod(NAK); /* Let's get it rolling */
#else
if (!(goodCRC = WCstartUp(CRC_START, 3, 4, &charIn, acceptable))) {
if (!WCstartUp(NAK, 7, 10, &charIn, acceptable)) {
if (cfg.debug) printf("Startup failed\n"); /*****/
return FALSE;
}
}
else printf("CRC mode\n");
#endif
do {
strCpy(errMess, "Unknown");
badSector = FALSE;
/* get synchronized: */
#ifdef NO_CRC
do {
firstchar = receive(10);
} while (
firstchar != SOH &&
firstchar != EOT &&
firstchar != ERROR
);
#else
if (lastSector) {
do {
firstchar = receive(10);
} while (
firstchar != SOH &&
firstchar != EOT &&
firstchar != ERROR
);
}
else
firstchar = charIn;
#endif
if (firstchar == ERROR) {
badSector = TRUE;
strCpy(errMess, "No data received");
}
if (firstchar == SOH) {
/* found StartOfHeader -- read sector# in: */
thisSector = receive (1);
thisComplement = receive (1); /* 1's comp of thisSector */
if ((thisSector + thisComplement) != 0xFF) {
badSector = TRUE;
sPrintf(errMess, "parity error on sector (%d %d)", thisSector,
thisComplement);
}
else {
if (thisSector == (lastSector + 1) % 256) {
/* right sector... let's read it in */
checksum = 0;
nextChar = sectBuf;
for (i = SECTSIZE; i; i--) {
*nextChar = receive (1);
checksum = (checksum + *nextChar++) & 0xFF;
}
recVal = receive (1);
#ifdef NO_CRC
if (recVal != checksum) {
badSector = TRUE;
sPrintf(errMess, "checksum error on data: expected %d, received %d",
checksum, recVal);
}
#else
if (goodCRC) {
if (recVal == ERROR) {
errorDet = FALSE;
strCpy(errMess, "recVal==ERROR on CRC");
}
else {
loCRC = receive(1);
if (loCRC == ERROR) {
errorDet = FALSE;
strCpy(errMess, "loCRC==ERROR on CRC");
}
else {
totalCRC = (recVal << 8) + loCRC;
temp = calcrc(sectBuf, 128);
errorDet = (totalCRC == temp);
sPrintf(errMess, "totalCRC==%x, calcrc==%x, recVal==%x, loCRC==%x\n",
totalCRC, temp, recVal, loCRC);
}
}
}
else {
errorDet = (recVal == checksum);
sPrintf(errMess, "recVal==%x, checksum==%x\n", recVal, checksum);
}
if (!errorDet) {
badSector = TRUE;
}
#endif
else {
tries = 0;
lastSector = thisSector % 256;
if (cfg.debug) printf("lastSector=%d\n", lastSector); /****/
if (cfg.debug) printf("Awaiting #%d (Try=0, Errs=%d) \r",
thisSector, toterr
);
if (tries && toterr) mputChar('\n');
/* write sector to where-ever: */
nextChar = sectBuf;
for (i=SECTSIZE; i; i--) {
if ((*pc)(*nextChar++) == ERROR) {
writeError = TRUE;
}
}
if (!writeError) outMod(ACK);
}
} else {
/* not expected sector... */
if (thisSector != lastSector) {
badSector = TRUE;
sPrintf(errMess, "VeryBadSector# %d", thisSector);
}
else {
/* aha -- sender missed an ACK and resent last: */
do; while (receive(1) != ERROR); /* eat it */
outMod(ACK); /* back in synch! */
}
}
} /* end of "if (thisSector + thisComplement == 255" */
} /* end of "if (firstChar == SOH)" */
if (badSector) {
if (cfg.debug) printf("\n%s\n", errMess);
strCpy(errMess, "Unknown");
tries++;
if (lastSector != 0) toterr++;
while (receive (1) != ERROR);
if (cfg.debug) printf("Awaiting #%d (Try=%d, Errs=%d) \r",
lastSector, tries, toterr
);
if (tries && toterr) mputChar('\n');
if (gotCarrier())
outMod(NAK);
else
tries = ERRORMAX; /* No carrier, so force out. */
}
} while (
firstchar != EOT &&
tries < ERRORMAX &&
!writeError
);
if (writeError) {
printf("\nwrite error\n");
outMod(CAN);
}
else if (firstchar != EOT || tries >= ERRORMAX) {
printf("\naborting\n");
return FALSE;
} else {
outMod(ACK);
if (!inNet) printf("\nfile reception complete.\n");
return TRUE;
}
}
/************************************************************************/
/* WCstartUp() Starts up WC and (sometime) YMODEM */
/************************************************************************/
WCstartUp(startChar, tries, timeout, charIn, lookFor)
char startChar, *charIn;
int tries, timeout;
char *lookFor;
{
int retVal;
char *strchr();
if (cfg.debug) printf("In WCstartup\n");
for (; tries && gotCarrier(); tries--) {
if (cfg.debug) printf("%d: %d\n", tries, startChar);
outMod(startChar);
retVal = receive(10);
if (cfg.debug) printf("Received %d\n", retVal);
if (retVal != ERROR) {
*charIn = retVal;
if (strchr(lookFor, *charIn) != NULL)
return TRUE;
}
}
return FALSE;
}
/************************************************************************/
/* ringSysop() signals a chat mode request. Exits on input from */
/* modem or keyboard. */
/************************************************************************/
#define RING_LIMIT 6
ringSysop()
{
char BBSCharReady(), answered;
int i, ring;
mPrintf("\n Ringing sysop.\n ");
answered = FALSE;
for (ring = 0; ring < RING_LIMIT && !answered && gotCarrier(); ring++) {
for (i=0;
ring < RING_LIMIT && !BBSCharReady() && !KBReady();
i = ++i % 7) {
/* play shave-and-a-haircut/two bits... as best we can: */
oChar(BELL);
pause(cfg.shave[i]);
if (i == 6) ring++;
}
if (BBSCharReady() || KBReady()) answered = TRUE;
}
if (KBReady()) {
getCh();
whichIO = CONSOLE;
interact();
whichIO = MODEM;
}
else if (ring >= RING_LIMIT) {
cfg.noChat = TRUE;
mPrintf("\n Sorry, Sysop not around...\n ");
}
else modIn();
}
/************************************************************************/
/* sendWCChar() sends a file using Ward Christensen's protocol. */
/* (i.e., compatable with xModem, modem7, modem2, YAM, ... ) */
/************************************************************************/
int sendWCChar(c)
int c; /* character to output to MODEM, or ERROR on EOF */
{
unsigned int CRC;
int i, j, m;
unsigned char ck;
if (WCError)
return FALSE;
WCBuf[WCChar++] = c;
if (WCChar != SECTSIZE)
return TRUE;
for (i = 0; i < ERRORMAX; i++) { /* Time to transmit */
if (cfg.debug) printf("Sending #%3d (errors =%3d)\r", WCSecNum, i);
outMod(SOH);
outMod(WCSecNum);
outMod(~WCSecNum);
for (j = ck = 0; j < SECTSIZE; j++) {
outMod(WCBuf[j]);
ck += WCBuf[j];
}
if (CRCmode) {
CRC = calcrc(WCBuf, 128);
outMod(((CRC & 0xFF00) >> 8));
outMod(CRC & 0x00FF);
}
else
outMod(ck);
m = receive(MINUTE);
if (m == ACK || m == CAN || !gotCarrier()) break;
}
WCChar = 0;
WCSecNum++;
if (m == ACK)
return TRUE;
WCError = TRUE;
mPrintf("Aborting\n ");
return FALSE;
}
/************************************************************************/
/* shortPause() busy-waits 2/1000 seconds |br| */
/* This should fix the 1200 network problem <HAW> */
/************************************************************************/
shortPause()
{
#define MSECFACTOR ((SECONDSFACTOR*2)/10)
int j;
for (j=(MSECFACTOR*cfg.megaHz); j; j--);
}